package me.ree.zkproduct.annotation;

import lombok.extern.slf4j.Slf4j;
import me.ree.zkproduct.util.ReflectionUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * BeanPostProcessor.postProcessAfterInitialization在每个Bean完成初始化后都会执行，
 * 如果只是在所有Bean完成初始化后需要执行一次的逻辑，
 * 写在@PostConstruct或者InitializingBean.afterPropertiesSet中比较好
 */
@Slf4j
@Component
public class MyCustomAnnotationProcessor implements BeanPostProcessor {

    private Map<String, MyCustomAnnotation> myCustomAnnotationMethodMap;

    /**
     * 验证Class的isAnnotationPresent和getAnnotation方法是扫描范围
     * 由于BeanPostProcessor.postProcessAfterInitialization在每个Bean完成初始化后都会执行，
     * 为了提高服务的启动效率，平时一般注释整个方法，使其不被执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // bean.getClass().isAnnotationPresent只能扫描类上是否有MyCustomAnnotation注解
        if (bean.getClass().isAnnotationPresent(MyCustomAnnotation.class)) {
            // 获取类上的MyCustomAnnotation注解对象
            MyCustomAnnotation myCustomAnnotation = bean.getClass().getAnnotation(MyCustomAnnotation.class);
            log.info("Bean {} has custom annotation with value: {}", beanName, myCustomAnnotation.value());
        }

        return bean;
    }

    /**
     * 扫描包含MyCustomAnnotation注解的方法，
     * 将方法名称和Method对象保存到Map中
     */
    @PostConstruct
    public void init() {
        Set<Method> myCustomAnnotationMethodSet = ReflectionUtil.getMethodAnnotatedWith(MyCustomAnnotation.class);
        if (CollectionUtils.isEmpty(myCustomAnnotationMethodSet)) {
            return;
        }

        myCustomAnnotationMethodMap = new HashMap<>();
        myCustomAnnotationMethodSet.forEach(method -> {
            MyCustomAnnotation myCustomAnnotation = method.getAnnotation(MyCustomAnnotation.class);
            String key = String.format("%s.%s", method.getDeclaringClass(), method.getName());
            myCustomAnnotationMethodMap.put(key, myCustomAnnotation);
            log.info("method {} has custom annotation with value: {}", key, myCustomAnnotation.value());
        });
    }

    public MyCustomAnnotation getMyCustomAnnotationMethod(String key) {
        return myCustomAnnotationMethodMap.get(key);
    }
}
